Computed Properties – Ember.js入門(11)
渡辺です。
Ember.js入門の前回はObserverについて解説しましたが、今回はComputed Propertiesを扱います。Computed PropertiesはObserverの応用的な機能なので、Observer- Ember.js入門(10)を読んでいない方は、先に読まれることをオススメします。
さて、このComputed Propertiesですが、現時点では広く浸透した訳語がありません。「計算さるプロパティ」とか「自動算出プロパティ」といった訳語がパッと思いつきますがコレジャナイ感が漂っています。なので、Computed Propertiesとそのままの表記で解説します。ちょっと取っつきにくい名前かもしれませんが、理解してしまえば簡単で、かつ非常に便利な機能なのでマスターしておきましょう。
Computed Propertiesとは?
Computed Propertiesとは、関数(function)として振る舞うようなプロパティを定義する機能です。この説明をされても全く意味が解らないので、とりあえずサンプルコードを見てみましょう。
App.Person = Ember.Object.extend({ firstName: null, lastName: null, fullName: function() { return this.get('firstName') + ' ' + this.get('lastName'); }.property('firstName', 'lastName') }); var person = App.Person.create({firstName: 'Homura', lastName: 'Akemi'}); person.get('fullName'); // => "Homura Akemi"
App.PersonクラスにはfirstNameとlastNameのふたつのプロパティが定義されています。その後に定義されたfullNameがComputed Propertiesです。fullNameはfirstNameとlastNameを結合した結果を返します。
このようにComputed Propertiesは、関数(function)のように振る舞います。しかし、プロパティをgetする度に関数が実行されるのではなく、関連するプロパティが変更された時にComputed Propertiesの関数が再評価され、値が更新されます。言い換えればプロパティとして利用できるObserverです。
Computed Propertiesの使い方
Ember.Objectのサブクラスとしてクラスを定義すれば、Computed Propertiesを使うのは難しくありません。サンプルコードのように、プロパティ名に対し関数(function)のpropertyメソッドの実行結果を割り当てるだけです。この時、propertyメソッドの引数にはComputed Propertiesが依存するプロパティを指定します。
App.Order = Ember.Object.extend({ itemName: null, price: 0, quantity: 0, cost: function() { return this.get('price') * this.get('quantity'); }.property('price', 'quantity') });
また、Computed Propertiesはチェインさせることができるため、次のようにcostが変更された時に再評価されるdescriptionを定義することもできます。
App.Order = Ember.Object.extend({ itemName: null, price: 0, quantity: 0, cost: function() { return this.get('price') * this.get('quantity'); }.property('price', 'quantity'), description: function() { return this.get('itemName') + '[$' + this.get('cost') + ']'; }.property('itemName', 'cost') });
Computed Propertiesの使い所
Computed Propertiesは様々な状況で利用されます。ここでは代表的な使い方をサンプルコードと共に紹介します。
複数のプロパティをまとめる
最初に紹介したサンプルのように、firstNameとlastNameをあわせたfullNameを定義したい場合、Computed Propertiesは便利です。
App.Person = Ember.Object.extend({ firstName: null, lastName: null, fullName: function() { return this.get('firstName') + ' ' + this.get('lastName'); }.property('firstName', 'lastName') });
フォーマットする
MVCではModelはViewに依存しないことが大原則です。したがって、単位やカンマ区切りなどのフォーマットについて、Modelで処理することは御法度です。しかし、Ember.jsで使われているHandlebarテンプレートではViewに関数など動的なコードを埋め込むことができません。そのため、フォーマットについてはControllerの状態(属性)として定義することなります。この時、Computed Propertiesは便利に利用できます。
App.OrderController = Ember.ObjectController.extend({ formattedCost: function() { return '$' + numeral('0,0.00').format(this.get('cost')); }.property('cost') });
numeralはフォーマットなどを行うライブラリです。
CSSのクラスを定義する
フォーマットと同様にModelの状態から特定のスタイルシート(クラス)を当てたい場合にもComputed Propertiesは便利に利用できます。
App.OrderController = Ember.ObjectController.extend({ costClass: function() { return this.('sale') ? 'sale' : ''; }.property('sale') });
複雑な条件であっても簡単に記述できるでしょう。
小計を計算する
Modelが配列などの場合、Computed Propertiesで合計を計算させることができます。
App.OrdersController = Ember.ArrayController.extend({ totalCost: function() { var total = 0; this.get('model').forEach(function(o) { total += o.get('cost'); }); return total; }.property('model') });
まとめ
Computed PropertiesはObserverと同様に、他のプロパティが変更されたことをトリガーとして関数が実行される仕組みです。そして、その属性がプロパティとしても振る舞うため、getメソッドで値を取得することができます。また、依存するプロパティが変更された時のみ、値の再計算が行われるため、パフォーマンス的な問題もありません。
Computed Propertiesを活用する事で、Modelをシンプルに保ちつつControllerはViewに必要な情報を提供することができるでしょう。